package com.iteaj.iot.client.http.okhttp;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.iteaj.iot.client.http.*;
import okhttp3.*;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * http客户端
 * @see okhttp3.OkHttpClient
 */
public class OkHttpClient extends HttpClient {

    private okhttp3.OkHttpClient client;

    public OkHttpClient(HttpClientConnectProperties properties, HttpClientComponent component) {
        super(properties, component);
    }


    @Override
    public void get(HttpClientMessage requestMessage) {
        String url = requestMessage.getUrl();
        if(StrUtil.isBlank(url)) {
            throw new HttpProtocolException("请求url必填");
        }
        Response response = null;
        try {
            // 构建请求的url
            HttpUrl httpUrl = buildRequestUrl(url, requestMessage.getQueryParam());

            Request.Builder builder = new Request.Builder().get().url(httpUrl);
            handleRequestHeader(requestMessage, builder);

            response = httpInvoke(null, client.newCall(builder.build()), requestMessage);
            ResponseBody responseBody = response.body();

            requestMessage.build(response.code(), responseBody.string(), responseBody.contentType().type(), response.message());
        } catch (IOException e) {
            throw new HttpProtocolException(e.getMessage(), e);
        } finally {
            if(response != null) {
                response.body().close();
            }
        }
    }

    private void handleRequestHeader(HttpClientMessage requestMessage, Request.Builder builder) {
        Map<String, String> heads = requestMessage.getHeads();
        if(CollectionUtil.isNotEmpty(heads)) {
            heads.forEach((key, val) -> {
                builder.addHeader(key, val);
            });
        }
    }

    @Override
    public void get(HttpClientMessage requestMessage, Consumer<HttpClientMessage> handle) {
        String url = requestMessage.getUrl();
        if(StrUtil.isBlank(url)) {
            throw new HttpProtocolException("请求url必填");
        }

        if(null == handle) {
            throw new HttpProtocolException("未指定异步协议处理器: [ProtocolHandle]");
        }

        // 构建请求的Url
        HttpUrl httpUrl = buildRequestUrl(url, requestMessage.getQueryParam());

        try {
            Request.Builder builder = new Request.Builder().get().url(httpUrl);

            // 复制请求头
            handleRequestHeader(requestMessage, builder);

            httpInvoke(handle, client.newCall(builder.build()), requestMessage);
        } catch (Exception e) {
            throw new HttpProtocolException(e.getMessage(), e);
        }
    }

    private HttpUrl buildRequestUrl(String url, Map<String, Object> queryParam) {
        HttpUrl.Builder builder = HttpUrl.get(url).newBuilder();
        if(queryParam != null) {
            queryParam.forEach((key, value) -> {
                builder.addQueryParameter(key, (String) value);
            });
        }
        return builder.build();
    }

    @Override
    public void post(HttpClientMessage requestMessage) {
        String url = requestMessage.getUrl();
        if(StrUtil.isBlank(url)) {
            throw new HttpProtocolException("请求url必填");
        }

        Response response = null;
        try {
            // 构建请求body
            HttpUrl httpUrl = buildRequestUrl(url, requestMessage.getQueryParam());
            Request.Builder builder = new Request.Builder().post(requestMessage.getRequestBody()).url(httpUrl);
            handleRequestHeader(requestMessage, builder);

            response = httpInvoke(null, client.newCall(builder.build()), requestMessage);
            ResponseBody responseBody = response.body();
            requestMessage.build(response.code(), responseBody.string(), responseBody.contentType().type(), response.message());
        } catch (IOException e) {
            throw new HttpProtocolException(e.getMessage(), e);
        } finally {
            if(response != null) {
                response.body().close();
            }
        }
    }

    @Override
    public void post(HttpClientMessage requestMessage, Consumer handle) {
        String url = requestMessage.getUrl();
        if(StrUtil.isBlank(url)) {
            throw new HttpProtocolException("请求url必填");
        }

        if(null == handle) {
            throw new HttpProtocolException("未指定异步协议处理器: [ProtocolHandle]");
        }

        try {
            // 构建请求
            HttpUrl httpUrl = buildRequestUrl(url, requestMessage.getQueryParam());
            Request.Builder builder = new Request.Builder().post(requestMessage.getRequestBody()).url(httpUrl);

            // 处理请求头
            handleRequestHeader(requestMessage, builder);

            // http异步回调执行
            httpInvoke(handle, client.newCall(builder.build()), requestMessage);
        } catch (Exception e) {
            throw new HttpProtocolException(e.getMessage(), e);
        }
    }

    protected Response httpInvoke(Consumer<HttpClientMessage> handle, Call call, HttpClientMessage message) throws IOException {
        if(handle != null) {
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    handle.accept(message.build(e));
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    ResponseBody body = response.body();
                    handle.accept(message.build(response.code(), body.string(), body.contentType().type(), response.message()));
                }
            });

            return null;
        } else {
            return call.execute();
        }
    }

    @Override
    public void init(Object config) {
        client = new okhttp3.OkHttpClient().newBuilder()
                .connectTimeout(getConfig().getConnectTimeout(), TimeUnit.MILLISECONDS)
                .readTimeout(getConfig().getReaderIdleTime(), TimeUnit.SECONDS).build();
    }

    @Override
    public Object close() {
        client.dispatcher().cancelAll();
        ExecutorService executorService = client.dispatcher().executorService();
        if(!executorService.isShutdown()) {
            executorService.shutdown();
        }

        return this;
    }
}
